/* ----------------------------------------------------------------------------
* File: tcp_recv.cpp
*
* Desc: Stuffs for receiving by TCP transport
*
* Created: 11/03/2011, by Alexey Korshunov
* ---------------------------------------------------------------------------- */
#include "common.h"
#include "utils.h"


/************************************************************************/
/*	Desc: Receive data using TCP transport												
	Params:
		data - pointer to buffer for receiving
		size - data size
		timeout - time for waiting of receiving accessibility
		log_print - pointer to a output function
	Return:
		size of received data if successful,
		-1 if error														*/
/************************************************************************/
int UtilsTCPReceive(int sock, char *data, size_t size, int timeout, int *status, FILE *logFile)
{
	fd_set rfds;
	struct timeval tv;
	size_t bytes = size;
	int rv = 0, rd_bytes = 0;

	*status = UTILS_OK;

	// check socket descriptor
	if (sock <= 0)
	{
		*status = UTILS_ERROR;
		if (logFile != NULL)
		{
			fprintf(logFile, "UtilsTCPReceive: socket is incorrect, sock = %d\n", sock);
			fflush(logFile);
		}
		return -1;
	}

	while(bytes > 0)
	{
		FD_ZERO(&rfds);
		FD_SET(sock, &rfds);

		if (timeout != WAIT_FOREVER)
		{
			tv.tv_sec = timeout / 1000;							// seconds
			tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000;	// microseconds
			rv = select(sock + 1, &rfds, NULL, NULL, &tv);
		}
		else
			// wait forever
			rv = select(sock + 1, &rfds, NULL, NULL, NULL);

		if (rv < 0)	// error
		{
			*status = UTILS_ERROR;
			if (logFile != NULL)
			{
				fprintf(logFile, "UtilsTCPReceive: failed to select, sock = %d %s\n", sock, strerror(errno));
				fflush(logFile);
			}
			return -1;
		}

		if (rv == 0)	// timeout
		{
			*status = UTILS_TIMEOUT;
			return -1;
		}

		rv = recv(sock, (void*)(data + (size - bytes)), bytes, 0);
		if (rv == -1)	// error
		{
			if (errno == ECONNREFUSED || errno == ECONNRESET)
				*status = UTILS_CONNECTION_CLOSED;
			else
				*status = UTILS_ERROR;
			if (logFile != NULL)
			{
				fprintf(logFile, "UtilsTCPReceive: failed to receive data, sock = %d, %s\n", sock, strerror(errno));
				fflush(logFile);
			}
			return -1;
		}

		if (rv == 0)	// connection closed
		{
			*status = UTILS_CONNECTION_CLOSED;
			if (logFile != NULL)
			{
				fprintf(logFile, "UtilsTCPReceive: connection closed\n");
				fflush(logFile);
			}
			return -1;
		}

		bytes -= rv;
		rd_bytes += rv;
	}

	return rd_bytes;
}